#include "degfx/bitmap.h"

#define MXMODULE_FGETW
#define MXMODULE_BITPIXEL
#define MXMODULE_BITLINE

#include <assert.h>

static MX_BITMAP *mx__bitmap_pcx(const char *filename, unsigned convert)
{
	FILE *f;
	MX_BITMAP *b = 0;
	int c;
	int width, height;
	int bpp, bytes_per_line;
	int x, y;
	unsigned char pal[256][3];

	assert(filename);

	f = fopen(filename, "rb");
	if (!f)
		return 0;

	fgetc(f);					/* skip manufacturer ID */
	fgetc(f);					/* skip version flag */
	fgetc(f);					/* skip encoding flag */

	if (fgetc(f) != 8)			/* we like 8 bit color planes */
		goto done;

	width = -(mx_fgetw(f));		/* xmin */
	height = -(mx_fgetw(f));	/* ymin */
	width += mx_fgetw(f) + 1;	/* xmax */
	height += mx_fgetw(f) + 1;	/* ymax */

	mx_fgetw(f);				/* skip DPI values */
	mx_fgetw(f);

	for (c = 0; c < 16; c++) {	/* read the 16 color palette */
		pal[c][0] = fgetc(f);
		pal[c][1] = fgetc(f);
		pal[c][2] = fgetc(f);
	}

	fgetc(f);

	bpp = fgetc(f) * 8;			/* how many color planes? */
	if ((bpp != 8) && (bpp != 24))
		goto done;

	bytes_per_line = mx_fgetw(f);

	for (c = 0; c < 60; c++)	/* skip some more junk */
		fgetc(f);

	b = mx_bitmap(width - 1, height - 1);
	if (!b)
		goto done;

	mx_bitmap_clear(b, 0);

	for (y = 0; y < height; y++) {	/* read RLE encoded PCX data */
		x = 0;

		while (x < bytes_per_line * bpp / 8) {

			unsigned char ch = fgetc(f);

			if ((ch & 0xC0) == 0xC0) {
				c = (ch & 0x3F);
				ch = fgetc(f);

			} else
				c = 1;

			if (bpp == 8) {
				while (c--) {
					if (x < width)
						mx_bitmap_pixel(b, x, y, ch);

					x++;
				}

			} else {
				while (c--) {
					const int xx = x % bytes_per_line;

					if (xx < width) {
						const MX_PIXEL p = mx_bitmap_getpixel(b, xx, y);
						unsigned int rc = MXR(p);
						unsigned int gc = MXG(p);
						unsigned int bc = MXB(p);
						const int plane = x / bytes_per_line;

						if (plane == 0)
							rc = ch;

						else if (plane == 1)
							gc = ch;

						else if (plane == 2)
							bc = ch;

						mx_bitmap_pixel(b, xx, y, MXRGB(rc, gc, bc));
					}
					++x;
				}
			}
		}
	}

	/* look for a 256 color palette */
	if (bpp == 8) {
		while ((c = fgetc(f)) != EOF) {
			if (c == 12) {
				for (c = 0; c < 256; c++) {
					pal[c][0] = fgetc(f);
					pal[c][1] = fgetc(f);
					pal[c][2] = fgetc(f);
				}
				break;
			}
		}
	}

	if ((bpp == 8) && (convert)) {
		MX_BITMAP_ITER start = mx_bitmap_begin(b);
		const MX_BITMAP_ITER end = mx_bitmap_end(b);

		while (start != end) {
			const unsigned pr = pal[*start][0];
			const unsigned pg = pal[*start][1];
			const unsigned pb = pal[*start][2];

			*start++ = MXRGB(pr, pg, pb);
		}
	}

  done:
	fclose(f);
	return b;
}

MX_BITMAP *mx_bitmap_pcx(const char *filename)
{
	MX_BITMAP *ret;

	/**author PCX loading source taken from Allegro source and extensively modified. */
	ret = mx__bitmap_pcx(filename, true);
	MXDEBUG_ALLOCTAG(ret);

	return ret;
}

MX_BITMAP *mx_bitmap_pcx_greyscale(const char *filename)
{
	/** This function is primarily intended as a helper by font loading. */
	MX_BITMAP *b = mx__bitmap_pcx(filename, false);

	if (b) {
		/* Find out if we are a mono font or not */
		int monofont = 1;
		MX_BITMAP_ITER start = mx_bitmap_begin(b);
		const MX_BITMAP_ITER end = mx_bitmap_end(b);

		MXDEBUG_ALLOCTAG(b);

		while (start != end) {
			if ((*start != 0) && (*start != 0x01) && (*start != 0xff)) {
				monofont = 0;
				break;
			}
			++start;
		}

		start = mx_bitmap_begin(b);
		while (start != end) {

			/* Monochrome font have a slightly different encoding */
			if (monofont) {
				if (*start == 0)
					*start = MXRGB(0, 0, 0);
				else
					*start = MXRGB(0xff, 0xff, 0xff);

				/* Antialiased fonts have normal greyscale encoding */
			} else
				*start = MXRGB(*start, *start, *start);

			++start;
		}
	}
	return b;
}
